package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * Java反射机制demo
 * 反射是JAVA的动态机制，可以在程序【运行期间】再确定如：对象实例化，方法调用属性操作等。
 * 反射机制可以大大的提高代码的灵活度和可扩展性，但是随之带来的是较慢的运行效率和更多的系统开销。因此不能过度依赖反射机制。
 * */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException,
            NoSuchMethodException, InstantiationException, IllegalAccessException {
        String s = "----------------------------------------------------------------------";
        demo1();
        System.out.println(s);
        demo2();
        System.out.println(s);
        demo3();
        System.out.println(s);
        demo4();
    }

    /**
     * 获取类对象
     *
     * Class类
     *      Class类的实例被称为"类对象"。
     *      JVM在加载一个类的字节码文件到内部时就会实例化一个Class实例
     *      与加载的类对应，用这个Class实例记录加载的类的相关信息。并且
     *      在JVM内部每个被加载的类【都有且只有一个】类对象与之对应。
     *
     *
     * 获取一个类的类对象方式有:
     *      1:类名.class
     *          Class cls = String.class;
     *          Class cls = int.class;
     *
     *      2.Class.forName(String className)
     *          参数:类的完全限定名(包名.类名)
     *          Class cls = Class.forName("java.lang.String")
     *
     *      3:使用ClassLoader加载
     * */
    private static void demo1() throws ClassNotFoundException {
        Class c1 = HashMap.class;
        String classname = c1.getName();             // 获取类名
        System.out.println(classname);
        System.out.println(c1.getSimpleName());      // 获取简化类名

        Class<?> c2 = Class.forName("reflect.Person");
        System.out.println("\n" + c2.getName());
    }

    /**
     * 获取类中的方法
     *
     * Method类
     *      Method类的每一个实例称为"方法对象"。
     *      该类的每个实例表示某个类中的一个方法。通过方法对象我们可以得知其表
     *      示的方法的相关信息，如:方法名，返回值类型，参数个数，参数类型等等。
     *
     * Class中的方法:
     *      Method[] getMethods()   获取当前类对象所表示的类的所有公开方法
     * */
    private static void demo2() {
        Class c = HashMap.class;
        Method[] methods = c.getMethods();      /* 获取公开的方法 */
        System.out.println(c.getName() + "类公开的方法：");
        for (Method method : methods) {
            System.out.println(method.getName());
            int n = method.getParameterCount();     // 获取方法参数数量
            System.out.println("\t\t参数个数：" + n);
            if (n > 0) {
                System.out.print("\t\t参数类型：");
                Class<?>[] parameterTypes = method.getParameterTypes();     // 获取方法的参数类型
                for (Class<?> pt : parameterTypes)
                    System.out.print(pt.getSimpleName() + ", ");
                System.out.println();
            }
            System.out.println();
        }
    }

    /** 通过反射机制调用对象的无参方法 */
    private static void demo3() throws ClassNotFoundException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> c = Class.forName("reflect.Person");
        Object obj = c.newInstance();
        Method method = c.getMethod("sayHello");    // 获取指定方法，参数指方法名
        method.invoke(obj);    // 执行方法
    }

    /** 调用有参方法 */
    public static void demo4() throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = Class.forName("reflect.Person");
        Object obj = c.newInstance();
        Method method = c.getMethod("say", String.class);    // 获取指定方法，参数指方法名
        method.invoke(obj, "调用有参方法");    // 执行方法
        method = c.getMethod("say", String.class, int.class);
        method.invoke(obj, "1个亿", 3);
    }
}
